﻿using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using SqlSugar;
using SX.Core.EventBus.Db.Entity;
using SX.Core.EventBus.Events;
using System;
using System.Threading.Tasks;

namespace SX.Core.EventBus.Db
{
    /// <summary>
    /// event消息写入数据库
    /// </summary>
    public class EventLogService : IEventLogService
    {
        private SqlSugarClient Db;
        private ICache _cache;
        public EventLogService(IConfiguration configuration,ICache cache)
        {
            _cache = cache;
            Db = new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString =configuration.GetSection("SqlCon:ConnectionString").Value,
                DbType = DbType.SqlServer,
                IsAutoCloseConnection = true,
                ConfigId = 0,
                InitKeyType = InitKeyType.SystemTable,
                MoreSettings = new ConnMoreSettings()
                {
                    IsAutoRemoveDataCache = true
                }
            });
        }
        /// <summary>
        /// 保存Event消息到数据库
        /// </summary>
        /// <param name="event"></param>
        /// <returns></returns>
        public Task AddEventTodbAsync(IntegrationEvent @event)
        {
            EventLogEntity entity = new EventLogEntity
            {
                Id=@event.Id,
                BusinessId=@event.BusinessId,
                CreateTime=@event.CreateTime,
                Content= JsonConvert.SerializeObject(@event),
                State=EventStateEnum.NotPublished,
                MessageMark = @event.GetType().Name,
                Module=@event.Module
            };
            return Db.Insertable<EventLogEntity>(entity).ExecuteCommandAsync();
        }
        /// <summary>
        /// 标记消息为消费失败ConsumeFailed
        /// </summary>
        /// <param name="eventId"></param>
        /// <returns></returns>
        public Task MarkEventAsConsumeFailedAsync(string eventId)
        {
            return UpdateEventStatus(eventId, EventStateEnum.ConsumeFailed);
        }
        /// <summary>
        /// 标记消息为消费成功ConsumeSuccess
        /// </summary>
        /// <param name="eventId"></param>
        /// <returns></returns>
        public Task MarkEventAsConsumeSucessAsync(string eventId)
        {
            return UpdateEventStatus(eventId, EventStateEnum.ConsumeSucess);
        }    
        public Task MarkEventAsPublishedAsync(string eventId)
        {
            return UpdateEventStatus(eventId, EventStateEnum.Published);
        }

        public Task MarkEventAsPublishFailedAsync(string eventId)
        {
            return UpdateEventStatus(eventId,EventStateEnum.PublishedFailed);
        }

        private Task UpdateEventStatus(string eventId, EventStateEnum status)
        {
            var eventLogEntry = Db.Queryable<EventLogEntity>().Where(x => x.Id == eventId).Single();
            eventLogEntry.State = status;
            return Db.Updateable<EventLogEntity>(eventLogEntry).ExecuteCommandAsync();
        }
        /// <summary> 
        /// 处理event消息结果  这里属于发送方处理消息
        /// </summary>
        /// <param name="messageId"></param>
        /// <param name="result"></param>
        /// <param name="messageMark"></param>
        /// <returns></returns>
        public void HandleEventResult(string module,string messageId,bool isSuccess,string messageMark)
        {
            //从redis获取连数据库接字符串
            var sqlConnStr= _cache.Get(module);
            SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString = sqlConnStr,
                DbType = DbType.SqlServer,
                IsAutoCloseConnection = true
            });

            db.Ado.BeginTran();
            var model = db.Queryable<SysEventMessageMarkAndTableEntity, EventLogEntity>((a, b) => new object[] { JoinType.Inner, a.MessageMark == b.MessageMark })
                .Where((a, b) => b.Id.ToString() == messageId)
                .Select((a, b) => new SysEventMessageMarkAndTableEntity
                {
                    MessageMark = a.MessageMark,
                    OperationSql = a.OperationSql,
                    OperationSqlSucess = a.OperationSqlSucess,
                    Id =b.BusinessId
                }).First();
            bool result = false;
            if (isSuccess)
            {
                if (!string.IsNullOrEmpty(model?.OperationSqlSucess))
                {
                    if (model.Id.Split(',').Length <= 0)
                    {
                        result = db.Ado.ExecuteCommand(model.OperationSqlSucess, new { Id = model.Id }) > 0;
                    }
                    else
                    {
                        result = db.Ado.ExecuteCommand(model.OperationSqlSucess, new { Id = model.Id.Split(',') }) > 0;
                    }
                }
                result = Success(db,messageId);
            }
            else
            {
                if (!string.IsNullOrEmpty(model?.OperationSql))
                {
                    result = db.Ado.ExecuteCommand(model.OperationSql, new { Id = model.Id }) > 0;
                }
                result = Fail(db,messageId);
            }
            if (!result)
            {
                db.Ado.RollbackTran();
            }
            db.Ado.CommitTran();
        }
        private bool Fail(SqlSugarClient db, string messageId)
        {
            var isSuccess = db.Updateable<EventLogEntity>().SetColumns(i => new EventLogEntity() { State =EventStateEnum.ConsumeFailed }).Where(i => i.Id == messageId && i.State ==EventStateEnum.Published).ExecuteCommand() > 0;
            if (isSuccess)
                return true;
            else
                return false;
        }
        private bool Success(SqlSugarClient db, string messageId)
        {
            var isSuccess = db.Updateable<EventLogEntity>().SetColumns(i => new EventLogEntity() { State = EventStateEnum.ConsumeSucess }).Where(i => i.Id == messageId && (i.State == EventStateEnum.Published || i.State == EventStateEnum.ConsumeFailed)).ExecuteCommand() > 0;
            if (isSuccess)
                return true;
            else
                return false;
        }
    }
}
